package socklib

import (
	"context"
	"fmt"
	"net"
	"sync/atomic"
	"time"
)

/**
* 保持连接，扩展TCP Conn，
* 连接不可用时会自动重新连接
 */
const (
	CONN_STATE_OFFLINE   = 0
	CONN_STATE_RECONNECT = 1
	CONN_STATE_ONLINE    = 2
)

type (
	//通知读到新消息
	OnReadFn func(frame []byte)
	//
	KeepConn struct {
		conn    net.Conn
		network string
		address string
		state   int32
		fn      OnReadFn
		factory FrameFactory
	}
)

//
func NewKeepConn(network, address string, fn OnReadFn, factory FrameFactory, ctx context.Context) *KeepConn {
	kc := &KeepConn{
		network: network,
		address: address,
		factory: factory,
		fn:      fn,
	}
	kc.reconnect(ctx)
	return kc
}

//重新连接
func (kc *KeepConn) reconnect(ctx context.Context) {
	atomic.StoreInt32(&kc.state, CONN_STATE_RECONNECT)
	for {
		select {
		case <-ctx.Done():
			return
		default:
			conn, err := net.DialTimeout(kc.network, kc.address, time.Second)
			if err == nil {
				kc.conn = conn
				atomic.StoreInt32(&kc.state, CONN_STATE_ONLINE)
				return
			}
			time.Sleep(time.Second)
			fmt.Print("R")
		}
	}
}

//
func (kc *KeepConn) waitting() {
	for {
		if atomic.LoadInt32(&kc.state) == CONN_STATE_ONLINE {
			return
		}
		time.Sleep(time.Second)
	}
}

//写入数据
func (kc *KeepConn) Write(data []byte, ctx context.Context) {
	bs := kc.factory.BuildFrame(data)
	for {
		kc.waitting()
		_, err := kc.conn.Write(bs)
		if err == nil {
			return
		}
		kc.reconnect(ctx)
	}
}

//
func (kc *KeepConn) Read(ctx context.Context) {
	for {
		kc.waitting()
		bs, err := kc.factory.ReadFrame(kc.conn)
		if err == nil && kc.fn != nil {
			kc.fn(bs)
		} else if err != nil {
			kc.reconnect(ctx)
		}
	}
}
